package zhoug.chart.renderer;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;

import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart;
import com.github.mikephil.charting.utils.FSize;
import com.github.mikephil.charting.utils.MPPointD;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;
import zhoug.chart.Chart2Sdk;
import zhoug.chart.axis.MPXAxis;
import zhoug.chart.formatters.MPAxisTextValueFormatter;
import zhoug.logging.Logger;

/**
 * 水平柱状图x轴绘制器
 *
 * @Author: zhoug
 * @Date: 2024-07-29
 * @Description:
 */
public class MPXAxisRendererHorizontalBarChart extends XAxisRendererHorizontalBarChart {
    private static final String TAG = ">>>MPXAxisRenderer";
    private static boolean DEBUG = Chart2Sdk.isDebug();
    private Paint mRinkingPaint;
    private boolean mRinkingInit = false;

    public MPXAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, BarChart chart) {
        super(viewPortHandler, xAxis, trans, chart);
        mRinkingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    @Override
    public void computeAxis(float min, float max, boolean inverted) {
        if (DEBUG) {
            Logger.d(TAG, "computeAxis:min=" + min + ",max=" + max);
        }
        // calculate the starting and entry point of the y-labels (depending on
        // zoom / contentrect bounds)
        if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) {

            MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom());
            MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop());

            if (inverted) {

                min = (float) p2.y;
                max = (float) p1.y;
            } else {

                min = (float) p1.y;
                max = (float) p2.y;
            }
            if (DEBUG) {
                Logger.d(TAG, "computeAxis:p1=" + p1);
                Logger.d(TAG, "computeAxis:p2=" + p2);
                Logger.d(TAG, "computeAxis:min=" + min + ",max=" + max);
            }
            MPPointD.recycleInstance(p1);
            MPPointD.recycleInstance(p2);
        }
        computeAxisValues(min, max);
    }

    @Override
    protected void computeSize() {
        if (DEBUG) {
            Logger.d(TAG, "computeSize:");
        }
        mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
        mAxisLabelPaint.setTextSize(mXAxis.getTextSize());

        String longest = mXAxis.getLongestLabel();

        final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest);

        final float labelWidth = (int) (labelSize.width + mXAxis.getXOffset() * 3.5f);
        final float labelHeight = labelSize.height;
        if (DEBUG) {
            Logger.d(TAG, "computeSize:labelWidth=" + labelWidth + ",labelHeight=" + labelHeight);
        }

        final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees(
                labelSize.width,
                labelHeight,
                mXAxis.getLabelRotationAngle());

        mXAxis.mLabelWidth = Math.round(labelWidth);
        mXAxis.mLabelHeight = Math.round(labelHeight);
        mXAxis.mLabelRotatedWidth = (int) (labelRotatedSize.width + mXAxis.getXOffset() * 3.5f);
        mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height);

        FSize.recycleInstance(labelRotatedSize);
    }

    @Override
    public void renderAxisLabels(Canvas c) {
        if (DEBUG) {
            Logger.d(TAG, "renderAxisLabels:");
        }
        if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled())
            return;

        float xoffset = mXAxis.getXOffset();

        mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
        mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
        mAxisLabelPaint.setColor(mXAxis.getTextColor());

        MPPointF pointF = MPPointF.getInstance(0, 0);

        if (mXAxis.getPosition() == XAxis.XAxisPosition.TOP) {
            pointF.x = 0.0f;
            pointF.y = 0.5f;
            drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF);

        } else if (mXAxis.getPosition() == XAxis.XAxisPosition.TOP_INSIDE) {
            pointF.x = 1.0f;
            pointF.y = 0.5f;
            drawLabels(c, mViewPortHandler.contentRight() - xoffset, pointF);

        } else if (mXAxis.getPosition() == XAxis.XAxisPosition.BOTTOM) {
            pointF.x = 1.0f;
            pointF.y = 0.5f;
            drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF);

        } else if (mXAxis.getPosition() == XAxis.XAxisPosition.BOTTOM_INSIDE) {
            pointF.x = 1.0f;
            pointF.y = 0.5f;
            drawLabels(c, mViewPortHandler.contentLeft() + xoffset, pointF);

        } else { // BOTH SIDED
            pointF.x = 0.0f;
            pointF.y = 0.5f;
            drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF);
            pointF.x = 1.0f;
            pointF.y = 0.5f;
            drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF);
        }

        MPPointF.recycleInstance(pointF);
    }

    /**
     * 位置x轴文字
     *
     * @param c
     * @param pos
     * @param anchor
     */
    @Override
    protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
        if (DEBUG) {
            Logger.d(TAG, "drawLabels:pos=" + pos);
        }
        final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
        boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled();
        if (DEBUG) {
            Logger.d(TAG, "drawLabels:centeringEnabled=" + centeringEnabled);
            Logger.d(TAG, "drawLabels:mEntryCount=" + mXAxis.mEntryCount);
        }
        float[] positions = new float[mXAxis.mEntryCount * 2];

        for (int i = 0; i < positions.length; i += 2) {

            // only fill x values
            if (centeringEnabled) {
                positions[i + 1] = mXAxis.mCenteredEntries[i / 2];
            } else {
                positions[i + 1] = mXAxis.mEntries[i / 2];
            }
            if (DEBUG) {
                Logger.d(TAG, "drawLabels:position:" + positions[i] + "," + positions[i + 1]);
            }
        }

        mTrans.pointValuesToPixel(positions);

        for (int i = 0; i < positions.length; i += 2) {

            float y = positions[i + 1];

            if (mViewPortHandler.isInBoundsY(y)) {
                ValueFormatter valueFormatter = mXAxis.getValueFormatter();
                String label = valueFormatter.getAxisLabel(mXAxis.mEntries[i / 2], mXAxis);
                String rinking = null;
                if (valueFormatter instanceof MPAxisTextValueFormatter cusFormatter) {
                    rinking = cusFormatter.getRinking(mXAxis.mEntries[i / 2], mXAxis);
                }
                drawLabel(c, rinking, label, pos, y, anchor, labelRotationAngleDegrees);

            }
        }
    }

    protected void drawLabel(Canvas c, String ringking, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
        if (DEBUG) {
            Logger.d(TAG, "drawLabel:" + formattedLabel + ",x=" + x + ",y=" + y + ",anchor=" + anchor.x + "," + anchor.y + ",angleDegrees=" + angleDegrees);
        }
        drawXAxisValue(c, ringking, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees);
    }

    private static Rect mDrawTextRectBuffer = new Rect();
    private static Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics();

    public void drawXAxisValue(Canvas c, String rinking, String text, float x, float y,
                               Paint paint,
                               MPPointF anchor, float angleDegrees) {

        float drawOffsetX = 0.f;
        float drawOffsetY = 0.f;
        int rinkingWidth = 0;
        boolean drawRinking = false;

        if (mXAxis instanceof MPXAxis mpxAxis) {
            drawRinking = mpxAxis.isDrawRinking();
            if(!mRinkingInit){
                int rinkingTextColor = mpxAxis.getRinkingTextColor();
                int rinkingTextSize = mpxAxis.getRinkingTextSize();
                Typeface rinkingTextTypeFace = mpxAxis.getRinkingTextTypeFace();
                mRinkingPaint.setColor(rinkingTextColor != 0 ? rinkingTextColor : paint.getColor());
                mRinkingPaint.setTextSize(rinkingTextSize > 0 ? rinkingTextSize : paint.getTextSize());
                mRinkingPaint.setTypeface(rinkingTextTypeFace != null ? rinkingTextTypeFace : paint.getTypeface());
                mRinkingInit=true;
            }

        }
        if (drawRinking && rinking!=null) {
            mRinkingPaint.getTextBounds(rinking, 0, rinking.length(), mDrawTextRectBuffer);
            rinkingWidth = mDrawTextRectBuffer.width();
        }
        final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer);
        paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer);

        // Android sometimes has pre-padding
        drawOffsetX -= mDrawTextRectBuffer.left;

        // Android does not snap the bounds to line boundaries,
        //  and draws from bottom to top.
        // And we want to normalize it.
        drawOffsetY += -mFontMetricsBuffer.ascent;

        // To have a consistent point of reference, we always draw left-aligned
        Paint.Align originalTextAlign = paint.getTextAlign();
        paint.setTextAlign(Paint.Align.LEFT);
        if (DEBUG) {
            Logger.d(TAG, "drawXAxisValue:x=" + x + ",y=" + y);
            Logger.d(TAG, "drawXAxisValue:drawOffsetX=" + drawOffsetX + ",drawOffsetY=" + drawOffsetY);

        }
        if (angleDegrees != 0.f) {

            // Move the text drawing rect in a way that it always rotates around its center
            drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f;
            drawOffsetY -= lineHeight * 0.5f;

            float translateX = x;
            float translateY = y;

            // Move the "outer" rect relative to the anchor, assuming its centered
            if (anchor.x != 0.5f || anchor.y != 0.5f) {
                final FSize rotatedSize = Utils.getSizeOfRotatedRectangleByDegrees(
                        mDrawTextRectBuffer.width(),
                        lineHeight,
                        angleDegrees);

                translateX -= rotatedSize.width * (anchor.x - 0.5f);
                translateY -= rotatedSize.height * (anchor.y - 0.5f);
                FSize.recycleInstance(rotatedSize);
            }

            c.save();
            c.translate(translateX, translateY);
            c.rotate(angleDegrees);

            c.drawText(text, drawOffsetX, drawOffsetY, paint);

            c.restore();
        } else {
            if (anchor.x != 0.f || anchor.y != 0.f) {
                drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x;
                drawOffsetY -= lineHeight * anchor.y;
            }

            drawOffsetX += x;
            drawOffsetY += y;
            if (drawRinking && rinking!=null) {
                c.drawText(rinking, 0, drawOffsetY, mRinkingPaint);
                if (drawOffsetX < rinkingWidth) {
                    drawOffsetX = rinkingWidth;
                }
            }
            c.drawText(text, drawOffsetX, drawOffsetY, paint);
        }

        paint.setTextAlign(originalTextAlign);
    }

}
